
package com.haiyou.common.util.date;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Calendar;
import java.util.Date;
import java.util.concurrent.TimeUnit;

import com.haiyou.common.util.string.StringUtils;

import lombok.extern.slf4j.Slf4j;

/**
 * 
 * 
 * @Description: 日期工具类
 * @author xingyuan
 * @date 2022年2月15日 下午5:20:23
 */
@Slf4j
public final class DateUtils {

	/**
	 * 一秒钟
	 */
	public static final int MILLISECOND_OF_ONE_SECONDS = 1000;

	/**
	 * 一分钟
	 */
	public static final int MILLISECOND_OF_ONE_MINUTE = MILLISECOND_OF_ONE_SECONDS * 60;

	/**
	 * 一小时
	 */
	public static final int MILLISECOND_OF_ONE_HOUR = MILLISECOND_OF_ONE_MINUTE * 60;

	/**
	 * 一天
	 */
	public static final long MILLISECOND_OF_ONE_DAY = MILLISECOND_OF_ONE_HOUR * 24;

	/**
	 * 精确到秒的日期格式
	 */
	public static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
	/**
	 * 精确到秒的日期格式
	 */
	public static final String DATEFORMAT = "yyyyMMddHHmmss";
	/**
	 * 精确到毫秒的日期格式
	 */
	public static final String DATE_FORMAT_SSS = "yyyy-MM-dd HH:mm:ss:SSS";
	/**
	 * 精确到毫秒的日期格式
	 */
	public static final String DATEFORMATSSS = "yyyyMMddHHmmssSSS";

	/**
	 * 星期枚举 "星期日,1", "星期一,2", "星期二,3", "星期三,4", "星期四,5", "星期五,6", "星期六,7"
	 * 
	 */
	static public enum EnumWeek {
		/** 周日 */
		SUN(Calendar.SUNDAY, "星期日"),
		/** 周一 */
		MON(Calendar.MONDAY, "星期一"),
		/** 周二 */
		TUE(Calendar.TUESDAY, "星期二"),
		/** 周三 */
		WED(Calendar.WEDNESDAY, "星期三"),
		/** 周四 */
		THU(Calendar.THURSDAY, "星期四"),
		/** 周五 */
		FRI(Calendar.FRIDAY, "星期五"),
		/** 周六 */
		SAT(Calendar.SATURDAY, "星期六"),;

		/**
		 * 星期几的数学表达式
		 */
		public final int value;

		/**
		 * 星期几的中文表达式
		 */
		public final String wstr;

		private EnumWeek(int value, String wstr) {
			this.value = value;
			this.wstr = wstr;
		}

		/**
		 * 根据星期几的数学表达式转成枚举
		 * 
		 * @param week
		 * @return
		 */
		static public EnumWeek valueOf(int week) {

			for (EnumWeek weekEnum : EnumWeek.values()) {
				if (weekEnum.value == week) {
					return weekEnum;
				}
			}
			return null;
		}
	}

	/**
	 * 月份天数定义
	 */
	public static final int[] MONTHS = { //
			31, // 1月
			28, // 2月
			31, // 3月
			30, // 4月
			31, // 5月
			30, // 6月
			31, // 7月
			31, // 8月
			30, // 9月
			31, // 10月
			30, // 11月
			31, // 12月
	};

	/** 格林尼治时间偏移量 1970-01-01 00:00:00 时间戳 */
	public static long offsetTime;
	static {
		try {
			offsetTime = time0();
		} catch (ParseException e) {
			log.error("Parse time for [1970-01-01 00:00:00]", e);
		}
	}

	/**
	 * 获取当前时间从0点开始计算的总秒数
	 * 
	 * @return
	 */
	public static int getDaySecond() {
		long millisSecond = System.currentTimeMillis() - timeAt0();
		return (int) (millisSecond / MILLISECOND_OF_ONE_SECONDS);
	}

	/**
	 * 时间为null或者time<1
	 * 
	 * @param date
	 * @return
	 */
	public static boolean isBlank(Date date) {
		return date == null || date.getTime() < 1;
	}

	/**
	 * 时间为<1
	 * 
	 * @param time
	 * @return
	 */
	public static boolean isBlank(long time) {
		return time < 1;
	}

	/**
	 * 时间t是否处于当天h1:00:00~h2:00:00之间
	 * 
	 * @param t  时间戳
	 * @param h1 开始小时点
	 * @param h2 结束小时点
	 * @return
	 */
	public static boolean inHourTimes(long t, int h1, int h2) {
		return inTimes(t, h1 * MILLISECOND_OF_ONE_HOUR, h2 * MILLISECOND_OF_ONE_HOUR);
	}

	/**
	 * 时间t是否处于当天h1:m1~h2:m2之间
	 * 
	 * @param t  时间戳
	 * @param h1 开始小时点
	 * @param m1 开始分钟
	 * @param h2 结束小时点
	 * @param m2 结束分钟
	 * @return
	 */
	public static boolean inHourMinuteTimes(long t, int h1, int m1, int h2, int m2) {
		return inTimes(t, h1 * MILLISECOND_OF_ONE_HOUR + m1 * MILLISECOND_OF_ONE_MINUTE,
				h2 * MILLISECOND_OF_ONE_HOUR + m2 * MILLISECOND_OF_ONE_MINUTE);
	}

	/**
	 * 检查时间t是否处于当天t1~t2之间
	 * 
	 * @param t  待检查的时间
	 * @param t1 当天00:00:00 ~ (t1)xx:xx:xx 的时间戳
	 * @param t2 当天00:00:00 ~ (t2)xx:xx:xx 的时间戳
	 * @return
	 */
	public static boolean inTimes(long t, int t1, int t2) {
		long t0 = timeAt0(t);
		int passTime = (int) (t - t0);
		int beginHour = t1 > t2 ? t2 : t1;
		int endHour = t1 > t2 ? t1 : t2;
		return passTime >= beginHour && passTime <= endHour;
	}

	/**
	 * 获取起始日期的0点时间戳,取1970-01-01 00:00:00
	 * 
	 * @return
	 * @throws ParseException
	 */
	public static long time0() throws ParseException {
		return timeMills(DATE_FORMAT, "1970-01-01 00:00:00");
	}

	/**
	 * 1970-01-01 00:00:00
	 * 
	 * @return
	 */
	public static Date firstDate0() {
		return new Date(offsetTime);
	}

	/**
	 * 1970-01-01 08:00:00
	 * 
	 * @return
	 */
	public static Date firstDate() {
		return new Date(0);
	}

	/**
	 * 根据当前时间,获取下几周指定星期几的0点时间(从周日开始算第一天)
	 * 
	 * @param time      当前时间
	 * @param week      星期几
	 * @param nextValue 隔几周,如果是本周,就传0
	 * @return
	 */
	public static Date nextWeek(long time, EnumWeek week, int nextValue) {
		return nextWeek(time, week, nextValue, 0, 0, 0, 0);
	}

	/**
	 * 根据当前时间,获取下几周指定星期几的0点时间(从周日开始算第一天)
	 * 
	 * @param time      当前时间
	 * @param week      星期几
	 * @param nextValue 隔几周,如果是本周,就传0
	 * @param hour      小时
	 * @param minute    分钟
	 * @param second    秒
	 * @param mills     毫秒
	 * @return
	 */
	public static Date nextWeek(long time, EnumWeek week, int nextValue, int hour, int minute, int second, int mills) {
		long concurrentTime0 = timeAt0(new Date(time));
		Calendar cal = Calendar.getInstance();
		cal.setTimeInMillis(concurrentTime0);
		cal.set(Calendar.DAY_OF_WEEK, week.value);
		cal.set(Calendar.HOUR, hour);
		cal.set(Calendar.MINUTE, minute);
		cal.set(Calendar.SECOND, second);
		cal.set(Calendar.MILLISECOND, mills);
		return new Date(cal.getTimeInMillis() + TimeUnit.DAYS.toMillis(7 * nextValue));
	}

	public static Date nextWeek(long time, EnumWeek week, int nextValue, int hour, int minute, int second) {
		return nextWeek(time, week, nextValue, hour, minute, second, 0);
	}

	public static Date nextWeek(long time, EnumWeek week, int nextValue, int hour, int minute) {
		return nextWeek(time, week, nextValue, hour, minute, 0);
	}

	public static Date nextWeek(long time, EnumWeek week, int nextValue, int hour) {
		return nextWeek(time, week, nextValue, hour, 0);
	}

	/**
	 * 获取这个时间是星期几
	 * 
	 * @param time
	 * @return
	 */
	static public EnumWeek dayOfWeek(long time) {

		Calendar c = Calendar.getInstance();
		c.setTimeInMillis(time);
		int dayOfweek = c.get(Calendar.DAY_OF_WEEK);
		return EnumWeek.valueOf(dayOfweek);
	}

	/**
	 * 获取今天是本年的第几周
	 * 
	 * @param time
	 * @return
	 */
	public static int weekOfYear(long time) {

		Calendar c = Calendar.getInstance();
		c.setTimeInMillis(time);
		int weekOfyear = c.get(Calendar.WEEK_OF_YEAR) + 1;
		return weekOfyear;
	}

	/**
	 * 获取今天是本月的第几周
	 * 
	 * @param time
	 * @return
	 */
	public static int weekOfMonth(long time) {

		Calendar c = Calendar.getInstance();
		c.setTimeInMillis(time);
		int weekOfmonth = c.get(Calendar.WEEK_OF_MONTH) + 1;
		return weekOfmonth;
	}

	/**
	 * 年份
	 * 
	 * @param time
	 * @return
	 */
	public static int year(long time) {
		Calendar c = Calendar.getInstance();
		c.setTimeInMillis(time);
		int year = c.get(Calendar.YEAR);
		return year;
	}

	/**
	 * 月份
	 * 
	 * @param time
	 * @return
	 */
	public static int month(long time) {
		Calendar c = Calendar.getInstance();
		c.setTimeInMillis(time);
		int month = c.get(Calendar.MONTH);
		return month;
	}

	/**
	 * 今天几号
	 * 
	 * @param time
	 * @return
	 */
	public static int day(long time) {
		Calendar c = Calendar.getInstance();
		c.setTimeInMillis(time);
		int day = c.get(Calendar.DAY_OF_MONTH);
		return day;
	}

	/**
	 * 按指定格式转换日期表达式
	 * 
	 * @param format
	 * @param time
	 * @return
	 */
	public static String timeFormat(String format, long time) {
		return new SimpleDateFormat(format).format(time);
	}

	/**
	 * 按yyyy-MM-dd HH:mm:ss 格式转换日期表达式
	 * 
	 * @param time
	 * @return
	 */
	public static String timeFormat(long time) {
		return timeFormat(DATE_FORMAT, time);
	}

	/**
	 * 根据日期表达式转换时间戳
	 * 
	 * @param format
	 * @param date
	 * @return
	 * @throws ParseException
	 */
	public static long timeMills(String format, String date) throws ParseException {
		return new SimpleDateFormat(format).parse(date).getTime();
	}

	/**
	 * 根据日期表达式(yyyy-MM-dd HH:mm:ss)转换时间戳
	 * 
	 * @param date
	 * @return
	 * @throws ParseException
	 */
	public static long timeMills(String date) throws ParseException {

		return timeMills(DATE_FORMAT, date);
	}

	/**
	 * 根据日期表达式转换日期
	 * 
	 * @param format
	 * @param date
	 * @return
	 * @throws ParseException
	 */
	public static Date timeDate(String format, String date) throws ParseException {

		return new SimpleDateFormat(format).parse(date);
	}

	/**
	 * 根据日期表达式(yyyy-MM-dd HH:mm:ss)转换日期
	 * 
	 * @param date
	 * @return
	 * @throws ParseException
	 */
	public static Date timeDate(String date) throws ParseException {
		return timeDate(DATE_FORMAT, date);
	}

	/**
	 * 两个时间相隔天数,头一天23:59:59和下一天00:00:00也差一天,以两个时间在0点的时间戳对比得出
	 * 
	 * @param begin
	 * @param end
	 * @return
	 */
	public static int intervalDay(long begin, long end) {
		long startDay = (begin - offsetTime) / MILLISECOND_OF_ONE_DAY;
		long afterDay = (end - offsetTime) / MILLISECOND_OF_ONE_DAY;
		return (int) (afterDay - startDay);
	}

	/**
	 * 是否同一天
	 * 
	 * @param t1
	 * @param t2
	 * @return
	 */
	public static boolean isSameDay(long t1, long t2) {
		return intervalDay(t1, t2) == 0;
	}

	/** ？年？月的天数 */
	public static int monthOfDays(int year, int month) {
		if (month == 2) {
			int tmp = year % 4;
			if (tmp != 0) {
				// 平年 28天
				return MONTHS[1];
			}
			// 闰年 29天
			return MONTHS[1] + 1;
		}
		return MONTHS[month - 1];
	}

	/** 取？时？分？秒的时间戳 */
	public static long timeAt(long timeMills, int hour, int minute, int second) {
		Calendar cal = Calendar.getInstance();
		cal.setTimeInMillis(timeMills);
		cal.set(Calendar.HOUR_OF_DAY, hour);
		cal.set(Calendar.MINUTE, minute);
		cal.set(Calendar.SECOND, second);
		cal.set(Calendar.MILLISECOND, 0);
		return cal.getTimeInMillis();
	}

	/** 取日期？时？分？秒的时间戳 */
	public static long timeAt(Date date, int hour, int minute, int second) {
		return timeAt(date.getTime(), hour, minute, second);
	}

	/** 取当天 ？时？分？秒的时间戳 */
	public static long timeAt(int hour, int minute, int second) {
		return timeAt(System.currentTimeMillis(), hour, minute, second);
	}

	/** 取日期所在0点的时间戳 */
	public static long timeAt0(Date date) {
		return timeAt(date.getTime(), 0, 0, 0);
	}

	/** 取时间戳所在0点的时间戳 */
	public static long timeAt0(long timeMills) {
		return timeAt(timeMills, 0, 0, 0);
	}

	/** 取当天日期所在0点的时间戳 */
	public static long timeAt0() {
		return timeAt(System.currentTimeMillis(), 0, 0, 0);
	}

	/** 取明天0点的时间戳 */
	public static long getNextDayMorning(long time) {
		return getDiffDayMorning(time, 1);
	}

	/** 取指定时间偏移天数0点的时间戳 */
	public static long getDiffDayMorning(long time, int diffDay) {
		long morning = timeAt0(time);
		Calendar calendar = Calendar.getInstance();
		calendar.setTimeInMillis(morning);
		calendar.add(Calendar.DATE, diffDay);
		calendar.set(Calendar.MILLISECOND, 0);
		return calendar.getTimeInMillis();
	}

	/** 取指定时间下月1号0点的时间戳 */
	public static long getNextMonthMorning(long time) {
		return nextMonth(time, 1, 1, 0, 0, 0);
	}

	public static Date nextWeekInMon(long time, EnumWeek week, int nextValue) {
		long concurrentTime0 = timeAt0(new Date(time));
		Calendar cal = Calendar.getInstance();
		cal.setTimeInMillis(concurrentTime0);
		EnumWeek weekEnum = dayOfWeek(time);
		if (weekEnum == EnumWeek.SUN) {
			cal.add(Calendar.WEEK_OF_YEAR, -1);
		}
		cal.set(Calendar.DAY_OF_WEEK, week.value);
		cal.set(Calendar.HOUR, 0);
		cal.set(Calendar.MINUTE, 0);
		cal.set(Calendar.SECOND, 0);
		cal.set(Calendar.MILLISECOND, 0);
		return new Date(cal.getTimeInMillis() + TimeUnit.DAYS.toMillis(7 * nextValue));
	}

	/**
	 * 取指定时间几个月之后的时间戳
	 * 
	 * @param time   当前时间戳
	 * @param month  间隔几个月,也可以是负数表明前几个月
	 * @param day    几号
	 * @param hour   几点
	 * @param minute 几分
	 * @param second 几秒
	 * @return
	 */
	public static long nextMonth(long time, int month, int day, int hour, int minute, int second) {
		Calendar calendar = Calendar.getInstance();
		calendar.setTimeInMillis(time);
		calendar.add(Calendar.MONTH, month);
		calendar.set(Calendar.DAY_OF_MONTH, day);
		calendar.set(Calendar.HOUR_OF_DAY, hour);
		calendar.set(Calendar.MINUTE, minute);
		calendar.set(Calendar.SECOND, second);
		return calendar.getTimeInMillis();
	}

	public static long nextMonth(long time, int month, int day, int hour, int minute) {
		return nextMonth(time, month, day, hour, minute, 0);
	}

	public static long nextMonth(long time, int month, int day, int hour) {
		return nextMonth(time, month, day, hour, 0);
	}

	public static long nextMonth(long time, int month, int day) {
		return nextMonth(time, month, day, 0);
	}

	public static long nextMonth(long time, int month) {
		return nextMonth(time, month, 1);
	}

	/**
	 * 获取时间模版
	 * 
	 * @param millis
	 * @return
	 */
	public static DateTemplate getDateTemplate(long millis) {
		Calendar calendar = Calendar.getInstance();
		calendar.setTimeInMillis(millis);
		int year = calendar.get(Calendar.YEAR);
		int month = calendar.get(Calendar.MONTH) + 1;
		int day = calendar.get(Calendar.DAY_OF_MONTH);
		int hour = calendar.get(Calendar.HOUR_OF_DAY);
		int minute = calendar.get(Calendar.MINUTE);
		int second = calendar.get(Calendar.SECOND);
		int millSecond = calendar.get(Calendar.MILLISECOND);
		EnumWeek week = dayOfWeek(millis);
		return new DateTemplate(millis, year, month, day, hour, minute, second, millSecond, week);
	}

	/**
	 * 获取时间模版
	 * 
	 * @param date
	 * @return
	 */
	public static DateTemplate getDateTemplate(Date date) {
		return getDateTemplate(date.getTime());
	}

	/**
	 * 获取时间模版
	 * 
	 * @return
	 */
	public static DateTemplate getDateTemplate() {
		return getDateTemplate(System.currentTimeMillis());
	}

	/**
	 * 获取当前时间从0点开始计算的总秒数
	 * 
	 * @return
	 */
	public static int getSecondsFrom0() {
		long millisSecond = System.currentTimeMillis() - timeAt0();
		return (int) (millisSecond / MILLISECOND_OF_ONE_SECONDS);
	}

	/**
	 * 日期的模板
	 *
	 * @author xingyuan 
	 */
	public static class DateTemplate {

		public final long t;

		public final int year;

		public final int month;

		public final int day;

		public final int hour;

		public final int minute;

		public final int second;

		public final int millSecond;

		public final EnumWeek week;

		private DateTemplate(long t, int year, int month, int day, int hour, int minute, int second, int millSecond,
				EnumWeek week) {
			super();
			this.t = t;
			this.year = year;
			this.month = month;
			this.day = day;
			this.hour = hour;
			this.minute = minute;
			this.second = second;
			this.millSecond = millSecond;
			this.week = week;
		}

		@Override
		public String toString() {
			return timeFormat(DATE_FORMAT_SSS, t);
		}

		public String getStringTemplete() {
			return StringUtils.merge(year, month, day, hour, minute, second, millSecond, week);
		}
	}
	
	
	/**
	 * 设置0点0分0秒0毫秒
	 */
	public static long setTimeZero(long time) {
		Calendar calendar = Calendar.getInstance();
		calendar.setTimeInMillis(time);
		
		calendar.set(Calendar.HOUR_OF_DAY, 0);
		calendar.set(Calendar.MINUTE, 0);
		calendar.set(Calendar.SECOND, 0);
		calendar.set(Calendar.MILLISECOND, 0);

		return calendar.getTimeInMillis();
	}
	
	

	public static void main(String[] args) throws Exception {
		long now = System.currentTimeMillis();
		System.err.println(timeFormat(now));
		Date nextWeek = nextWeek(now, EnumWeek.TUE, 1, 12, 30);
		System.err.println(timeFormat(nextWeek.getTime()));
		//
		System.err.println("-------------------");
		//
		now = System.currentTimeMillis();
		System.err.println(timeFormat(now));
		long nextMonth = nextMonth(now, -2, 1, 0, 0, 0);
		System.err.println(timeFormat(nextMonth));

		LocalDate localDate = LocalDate.now();
		LocalTime localTime = LocalTime.now();
		LocalDateTime localDateTime = LocalDateTime.now();
		System.out.println(localDate);
		System.out.println(localTime);
		System.out.println(localDateTime);
	}

}
